package com.gitee.lichoking.mysoap.common;

import com.gitee.lichoking.mysoap.convert.DefaultConverterLoader;
import com.gitee.lichoking.mysoap.io.ClassLoaderWrapper;
import com.gitee.lichoking.mysoap.io.ResolverUtil;
import com.gitee.lichoking.mysoap.reflection.ReflectionException;
import com.gitee.lichoking.mysoap.reflection.ReflectionUtils;
import lombok.Getter;
import lombok.Setter;
import org.beetl.core.GroupTemplate;
import org.beetl.core.resource.StringTemplateResourceLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 配置
 *
 * @author lich
 * @date 2021/6/2
 */
@Setter
@Getter
public class Configuration {
    /**
     * 模板文件开始标志
     */
    private final String templateBeginFlag = "begin";
    /**
     * 模板文件结束标志
     */
    private final String templateEndFlag = "end";
    /**
     * mapper文件后缀
     */
    private final String mapperSuffix = "Mapper";
    /**
     * beetl模板GroupTemplate
     */
    private GroupTemplate groupTemplate;
    /**
     * mapperClass父类
     */
    private Class<?> baseMapper = Object.class;
    /**
     * mapperClass包名
     */
    private List<String> mapperClassPackages;
    /**
     * mapperClass类名
     */
    private List<String> mapperClassNames;
    /**
     * mapper.xml路径
     */
    private String mapperXmlPath = "";
    /**
     * SOAP请求地址
     */
    private final String soapUrl;
    /**
     * SOAP请求方法
     */
    private final String soapAction;
    /**
     * 所有mapperClass
     */
    private final Set<Class<?>> mapperInterfaces = new HashSet<>();
    /**
     * XML模板缓存
     */
    private final Map<String, XmlTemplate> xmlTemplateCache = new StrictMap<>(new HashMap<>(16));
    /**
     * 请求参数复杂类型field缓存
     */
    private final Map<Class<?>, Map<String, Method>> parameterFieldCache = new StrictMap<>(new HashMap<>(16));

    public Configuration(String soapUrl, String soapAction) {
        this.soapUrl = soapUrl;
        this.soapAction = soapAction;
    }

    public void defaultInit() {
        initGroupTemplate();
        initMappers();
        initTemplateCache();
        initParameterFieldCache();
    }

    private void initParameterFieldCache() {
        for (Class<?> mapperInterface : mapperInterfaces) {
            Method[] methods = mapperInterface.getMethods();
            for (Method method : methods) {
                Parameter[] parameters = method.getParameters();
                int parametersLength = parameters.length;
                if (parametersLength == 1 && !DefaultConverterLoader.hasConverter(parameters[0].getType())) {
                    addAllFiledByGetter(parameters[0].getType());
                }
            }
        }
    }

    private void addAllFiledByGetter(Class type) {
        Map<String, Method> fieldGetters = parameterFieldCache.get(type);
        if (fieldGetters == null) {
            fieldGetters = ReflectionUtils.getAllFiledByGetter(type);
            if (fieldGetters == null) {
                throw new ReflectionException(type.getName() + "反射获取field失败");
            }
            parameterFieldCache.put(type, fieldGetters);
        }
    }

    private void initGroupTemplate() {
        StringTemplateResourceLoader stringTemplateResourceLoader = new StringTemplateResourceLoader();
        org.beetl.core.Configuration cfg = null;
        try {
            cfg = org.beetl.core.Configuration.defaultConfiguration();
            cfg.add("/soap-beetl.properties");
        } catch (IOException e) {
            throw new SoapException("读取模板配置文件失败");
        }
        groupTemplate = new GroupTemplate(stringTemplateResourceLoader, cfg);
    }


    private void initMappers() {
        if (mapperClassPackages != null) {
            for (String mapperClassPackage : mapperClassPackages) {
                ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
                // 筛选出某个包下Object的子类，其实就是包下所有类
                resolverUtil.find(new ResolverUtil.IsA(baseMapper), mapperClassPackage);
                Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
                typeSet.forEach(type -> {
                    String simpleName = type.getSimpleName();
                    //mapper接口，后缀判断筛选
                    if (simpleName != null && simpleName.endsWith(mapperSuffix)) {
                        mapperInterfaces.add(type);
                    }
                });
            }
        }
        if (mapperClassNames != null) {
            ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
            for (String mapperClassName : mapperClassNames) {
                Class<?> c = null;
                try {
                    c = classLoaderWrapper.classForName(mapperClassName);
                } catch (ClassNotFoundException e) {
                    throw new SoapException(mapperClassName + "类找不到");
                }
                mapperInterfaces.add(c);
            }
        }
    }

    private void initTemplateCache() {
        for (Class<?> mapperInterface : mapperInterfaces) {
            ClassLoader classLoader = mapperInterface.getClassLoader();
            addTemplateCache(classLoader, mapperInterface);
        }
    }

    /**
     * 初始化模板缓存
     *
     * @param classLoader
     * @param mapperInterface
     */
    public void addTemplateCache(ClassLoader classLoader, Class<?> mapperInterface) {
        String mapperInterfaceSimpleName = mapperInterface.getSimpleName();
        String mapperInterfaceName = mapperInterface.getName();
        Method[] mapperInterfaceMethods = mapperInterface.getMethods();

        if (mapperInterfaceMethods.length == 0) {
            return;
        } else {
            String firstId = new StringBuilder(mapperInterfaceName).append(".").append(mapperInterfaceMethods[0].getName()).toString();
            if (!xmlTemplateCache.containsKey(firstId)) {
                String xmlResourcePath = new StringBuilder(mapperXmlPath).append(mapperInterfaceSimpleName).append(".xml").toString();

                InputStream inputStream = null;
                if (null != classLoader) {
                    // try to find the resource as passed
                    inputStream = classLoader.getResourceAsStream(xmlResourcePath);
                    // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
                    if (null == inputStream) {
                        inputStream = classLoader.getResourceAsStream("/" + xmlResourcePath);
                    }
                }
                if (inputStream == null) {
                    throw new SoapException(xmlResourcePath + "文件不存在");
                }
                SAXReader saxReader = new SAXReader();
                Document document = null;
                try {
                    document = saxReader.read(inputStream);
                } catch (DocumentException e) {
                    e.printStackTrace();
                }
                Element root = document.getRootElement();

                Iterator<Element> elementIterator = root.elementIterator();
                while (elementIterator.hasNext()) {
                    Element beginEle = elementIterator.next();
                    String beginEleName = beginEle.getName();
                    String beginEleMethod = beginEle.attribute("method").getText();

                    String id = "";
                    if (templateBeginFlag.equals(beginEleName)) {
                        id = new StringBuilder(mapperInterfaceName).append(".").append(beginEleMethod).toString();
                    }
                    StringBuilder bodySb = new StringBuilder();
                    while (elementIterator.hasNext()) {
                        Element nextEle = elementIterator.next();
                        String nextEleName = nextEle.getName();
                        if (templateEndFlag.equals(nextEleName)) {
                            break;
                        }
                        bodySb.append(nextEle.asXML());
                    }
                    XmlTemplate xmlBlock = XmlTemplate.builder().id(beginEleMethod).body(bodySb.toString()).build();
                    xmlTemplateCache.put(id, xmlBlock);
                }
            }
        }
    }

    /**
     * 不允许key重复的map
     *
     * @param <K>
     * @param <V>
     */
    protected static class StrictMap<K, V> implements Map<K, V> {

        private final Map<K, V> delegate;

        public StrictMap(Map<K, V> delegate) {
            this.delegate = delegate;
        }


        @Override
        public int size() {
            return delegate.size();
        }

        @Override
        public boolean isEmpty() {
            return delegate.isEmpty();
        }

        @Override
        public boolean containsKey(Object key) {
            return delegate.containsKey(key);
        }

        @Override
        public boolean containsValue(Object value) {
            return delegate.containsKey(value);
        }

        @Override
        public V get(Object key) {
            return delegate.get(key);
        }

        @Override
        public V put(K key, V value) {
            if (this.keySet().contains(key)) {
                throw new SoapException("重复的key：" + key);
            }
            return delegate.put(key, value);
        }

        @Override
        public V remove(Object key) {
            return delegate.remove(key);
        }

        @Override
        public void putAll(Map<? extends K, ? extends V> m) {
            delegate.putAll(m);
        }

        @Override
        public void clear() {
            delegate.clear();
        }

        @Override
        public Set<K> keySet() {
            return delegate.keySet();
        }

        @Override
        public Collection<V> values() {
            return delegate.values();
        }

        @Override
        public Set<Entry<K, V>> entrySet() {
            return delegate.entrySet();
        }
    }
}
